package com.jwgf.mqtt;

import android.util.Log;

import com.jwgf.common.AppContext;
import com.jwgf.common.IPConfig;
import com.jwgf.smarowindingmachine_unity.R;

import org.eclipse.paho.android.service.MqttAndroidClient;
import org.eclipse.paho.client.mqttv3.IMqttActionListener;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.IMqttToken;
import org.eclipse.paho.client.mqttv3.MqttCallbackExtended;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;

import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.SSLSocketFactory;

import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.functions.Consumer;

public class MqttClientManager {
    private static final MqttClientManager ourInstance = new MqttClientManager();

    private MqttAndroidClient mqttAndroidClient;

    private String topic;

    private MqttMessageListener listener;

    public static MqttClientManager getInstance() {
        return ourInstance;
    }

    public void setMqttMessageListener(MqttMessageListener listener) {
        this.listener = listener;
    }

    private MqttClientManager() {

    }

    public boolean isConnected() {
        if (mqttAndroidClient == null) {
            return false;
        } else {
            return mqttAndroidClient.isConnected();
        }
    }

    public void connect() {
        if (IPConfig.getIp() == null) {
            return;
        }
        mqttAndroidClient = new MqttAndroidClient(AppContext.getInstance(), IPConfig.getServerURI(), IPConfig.getMachineID());
//        mqttAndroidClient = new MqttAndroidClient(AppContext.getInstance(), "ssl://mqtt.jwrx.net:8883", IPConfig.getMachineID());
        mqttAndroidClient.setCallback(new MqttCallbackExtended() {
            @Override
            public void connectComplete(boolean reconnect, String serverURI) {
                subscribeToTopic();
                if (reconnect) {
                    Log.i("MQTT", "Reconnected to : " + serverURI);
                    // Because Clean Session is true, we need to re-subscribe
                } else {
                    Log.i("MQTT", "Connected to: " + serverURI);
                }
            }

            @Override
            public void connectionLost(Throwable cause) {

            }

            @Override
            public void messageArrived(String topic, MqttMessage message) throws Exception {
                listener.onMessage(topic, message);
            }

            @Override
            public void deliveryComplete(IMqttDeliveryToken token) {

            }
        });
        MqttConnectOptions mqttConnectOptions = new MqttConnectOptions();
        mqttConnectOptions.setAutomaticReconnect(true);
        mqttConnectOptions.setCleanSession(true);
        mqttConnectOptions.setKeepAliveInterval(30);
        mqttConnectOptions.setMaxInflight(1);
        mqttConnectOptions.setUserName("luotong");
        mqttConnectOptions.setPassword("luotonghwj".toCharArray());
//        InputStream ca = AppContext.getInstance().getResources().openRawResource(R.raw.ca);
//        InputStream cert = AppContext.getInstance().getResources().openRawResource(R.raw.cert);
//        InputStream key = AppContext.getInstance().getResources().openRawResource(R.raw.key);
//        try {
//            mqttConnectOptions.setSocketFactory(SslUtil.getSocketFactory(ca, cert, key, ""));
//        } catch (Exception e) {
//            e.printStackTrace();
//        } finally {
//            try {
//                ca.close();
//                cert.close();
//                key.close();
//            } catch (IOException e) {
//                e.printStackTrace();
//            }
//        }
        mqttConnectOptions.setWill("DisConnect", IPConfig.getIp().getBytes(), 0, false);
        try {
            //Log.i("MQTT","Connecting to " + serverUri);
            mqttAndroidClient.connect(mqttConnectOptions, null, new IMqttActionListener() {
                @Override
                public void onSuccess(IMqttToken asyncActionToken) {

                }

                @Override
                public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
                    Log.i("mqtt", exception.toString());
//                    Observable.timer(30, TimeUnit.SECONDS).subscribe(new Consumer<Long>() {
//                        @Override
//                        public void accept(Long aLong) throws Throwable {
//                            connect();
//                        }
//                    });
                }
            });
        } catch (MqttException ex) {
            ex.printStackTrace();
        }
    }

    public void subscribeToTopic() {
        try {
            mqttAndroidClient.subscribe("upgrade", 0);
            mqttAndroidClient.subscribe(IPConfig.getMachineID(), 0, null, new IMqttActionListener() {
                @Override
                public void onSuccess(IMqttToken asyncActionToken) {
                    MqttClientManager.this.topic = IPConfig.getMachineID();
                    Log.i("MQTT", "Subscribed!");
                }

                @Override
                public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
                    Log.i("MQTT", "Failed to subscribe");
                }
            });
        } catch (MqttException ex) {
            System.err.println("Exception whilst subscribing");
            ex.printStackTrace();
        }
    }

    public void unSubscribeTopic() {
        if (mqttAndroidClient != null && mqttAndroidClient.isConnected()) {
            try {
                mqttAndroidClient.unsubscribe(topic);
            } catch (MqttException e) {
                e.printStackTrace();
            }
        }
    }

    public void publishMessage(String topic, String payload, int qos) {
        if (!isConnected()) {
            connect();
        }
        if (isConnected()) {
            try {
                MqttMessage message = new MqttMessage();
                message.setPayload(payload.getBytes());
                message.setQos(0);
                mqttAndroidClient.publish(topic, message);
            } catch (MqttException e) {
                System.err.println("Error Publishing: " + e.getMessage());
                e.printStackTrace();
            }
        }

    }

    public void reset() {
        try {
            mqttAndroidClient.disconnect();
            connect();
        } catch (MqttException e) {
            e.printStackTrace();
        }
    }

    public interface MqttMessageListener {
        void onMessage(String topic, MqttMessage message);
    }
}
